package main

import (
	"errors"
	"fmt"
	"gitee.com/Ljolan/si-mqtt/corev5/authv5/authplus"
	"gitee.com/Ljolan/si-mqtt/corev5/messagev5"
	"gitee.com/Ljolan/si-mqtt/corev5/servicev5"
	"gitee.com/Ljolan/si-mqtt/corev5/topicsv5"
	"gitee.com/Ljolan/si-mqtt/logger"
	"github.com/lxn/walk"
	. "github.com/lxn/walk/declarative"
	"log"
	"strconv"
	"sync"
	"time"
)

var once sync.Once

func conM(mw *MainWind) {
	if mw.curAnimals == "" {
		log.Print("请选择需要连接的节点 ")
		mw.outTE.SetText("请选择需要连接的节点")
		return
	}
	if cons, ok := mw.conn[mw.curAnimals]; !ok {
		log.Print("当前连接的节点数据不存在 ")
		mw.outTE.SetText("当前连接的节点数据不存在")
		return
	} else {
		if cons.conOpen {
			log.Println("已经连接")
			mw.outTE.SetText("已经连接")
			return
		}
		once.Do(func() {
			logger.LogInit("debug") // 日志必须提前初始化
			topicsv5.TopicInit("")
			authplus.Register("", authplus.NewDefaultAuth())
		})

		// Instantiates a new Client
		cons.con = &servicev5.Client{}

		vp, _ := authplus.NewManager("")
		cons.con.AuthPlus = vp

		cmsg := messagev5.NewConnectMessage()
		cmsg.SetSessionExpiryInterval(uint32(cons.SessionExpiryTime))
		cmsg.SetUsername([]byte(cons.Username))
		cmsg.SetPassword([]byte(cons.Password))
		cmsg.SetClientId([]byte(cons.ClientId))
		cmsg.SetVersion(byte(cons.Version))
		cmsg.SetKeepAlive(uint16(cons.KeepAlive))
		cmsg.SetCleanSession(cons.CleanSession)
		cmsg.SetReceiveMaximum(uint16(cons.ReceiverMax))
		err := cons.con.Connect("tcp://"+cons.ServerAddr+":"+strconv.Itoa(cons.ServerPort), cmsg)
		if err != nil {
			log.Println(err)
			mw.outTE.SetText(err.Error())
			return
		}
		go func() {
			for {
				time.Sleep(time.Duration(int(cmsg.SessionExpiryInterval()*2/3)) * time.Second)
				cons.con.Ping(nil)
			}
		}()
		cons.conOpen = true
	}
}
func subM(mw *MainWind, topic string, qos int, share bool) {
	if mw.curAnimals == "" {
		log.Print("请选择需要连接的节点 ")
		mw.outTE.SetText("请选择需要连接的节点")
		return
	}
	if cons, ok := mw.conn[mw.curAnimals]; !ok {
		log.Print("当前连接的节点数据不存在 ")
		mw.outTE.SetText("当前连接的节点数据不存在")
		return
	} else {
		if !cons.conOpen {
			log.Println("节点未连接")
			mw.outTE.SetText("节点未连接")
			return
		}
		submsg := messagev5.NewSubscribeMessage()
		submsg.AddTopic([]byte(topic), byte(qos))

		// Subscribes to the topic by sending the messagev5. The first nil in the function
		// call is a OnCompleteFunc that should handle the SUBACK messagev5 from the server.
		// Nil means we are ignoring the SUBACK messages. The second nil should be a
		// OnPublishFunc that handles any messages send to the client because of this
		// subscription. Nil means we are ignoring any PUBLISH messages for this topic.
		fmt.Println("====== >>> Subscribe")
		err := cons.con.Subscribe(submsg, func(msg, ack messagev5.Message, err error) error {
			if err != nil {
				fmt.Println(err)
			} else {
				fmt.Println("\n====== >>> Subscribe msg handle")
				fmt.Println(ack)
			}
			return nil
		}, func(msg *messagev5.PublishMessage) error {
			fmt.Println("===<<<>>>", msg.String())
			mw.outTE.SetText(msg.String())
			return nil
		})
		if err != nil {
			log.Println(err)
			mw.outTE.SetText(err.Error())
			return
		}
	}
}
func unSubM(mw *MainWind, topic string, share bool) {
	if mw.curAnimals == "" {
		log.Print("请选择需要连接的节点 ")
		mw.outTE.SetText("请选择需要连接的节点")
		return
	}
	if cons, ok := mw.conn[mw.curAnimals]; !ok {
		log.Print("当前连接的节点数据不存在 ")
		mw.outTE.SetText("当前连接的节点数据不存在")
		return
	} else {
		if !cons.conOpen {
			log.Println("节点未连接")
			mw.outTE.SetText("节点未连接")
			return
		}
		submsg := messagev5.NewUnsubscribeMessage()
		submsg.AddTopic([]byte(topic))

		fmt.Println("====== >>> Unsubscribe")
		err := cons.con.Unsubscribe(submsg, func(msg, ack messagev5.Message, err error) error {
			if err != nil {
				fmt.Println(err)
			} else {
				fmt.Println("\n====== >>> Unsubscribe msg handle")
				fmt.Println(ack)
			}
			return nil
		})
		if err != nil {
			log.Println(err)
			mw.outTE.SetText(err.Error())
			return
		}
	}
}
func pubM(mw *MainWind, topic string, qos int, content string) {
	if mw.curAnimals == "" {
		log.Print("请选择需要连接的节点 ")
		mw.outTE.SetText("请选择需要连接的节点")
		return
	}
	if cons, ok := mw.conn[mw.curAnimals]; !ok {
		log.Print("当前连接的节点数据不存在 ")
		mw.outTE.SetText("当前连接的节点数据不存在")
		return
	} else {
		if !cons.conOpen {
			log.Println("未连接")
			mw.outTE.SetText("未连接")
			return
		}
		once.Do(func() {
			logger.LogInit("debug") // 日志必须提前初始化
			topicsv5.TopicInit("")
			authplus.Register("", authplus.NewDefaultAuth())
		})

		pubmsg := messagev5.NewPublishMessage()
		pubmsg.SetTopic([]byte(topic))
		pubmsg.SetPayload([]byte(content))
		pubmsg.SetQoS(byte(qos))

		// Publishes to the server by sending the messagev5
		fmt.Println("====== >>> Publish,", topic, qos, content)
		err := cons.con.Publish(pubmsg, func(msg, ack messagev5.Message, err error) error {
			return nil
		})
		if err != nil {
			log.Println(err)
			mw.outTE.SetText(err.Error())
			return
		}
	}
}

// rsrc -manifest main.manifest –ico icon.ico -o rsrc.syso  可以添加exe的icon
func main() {
	walk.AppendToWalkInit(func() {
		walk.FocusEffect, _ = walk.NewBorderGlowEffect(walk.RGB(0, 63, 255))
		walk.InteractionEffect, _ = walk.NewDropShadowEffect(walk.RGB(63, 63, 63))
		walk.ValidationErrorEffect, _ = walk.NewBorderGlowEffect(walk.RGB(255, 0, 0))
	})

	var mw = NewMainWind()

	if _, err := (MainWindow{
		AssignTo: &mw.MainWindow,
		Title:    "SIMQ",
		Layout:   Grid{Columns: 5},
		Bounds: Rectangle{
			X:      150,
			Y:      100,
			Width:  600,
			Height: 600,
		},
		Background: SystemColorBrush{},
		MinSize:    Size{1000, 600},
		MaxSize:    Size{1000, 600},
		Size:       Size{1000, 600},
		OnSizeChanged: func() {
			fmt.Println(mw.Size())
		},
		Children: []Widget{
			Composite{
				ColumnSpan: 1,
				AssignTo:   &mw.op,
				Background: SystemColorBrush{Color: walk.SysColorAppWorkspace},
				Layout:     VBox{},
				MaxSize:    Size{150, 100},
				MinSize:    Size{100, 100},
				OnSizeChanged: func() {
					mw.op.SetMinMaxSize(walk.Size(Size{100, 100}), walk.Size(Size{150, 100}))
				},
				Children: []Widget{
					PushButton{
						Text: "新连接",
						OnClicked: func() {
							animal := NewAnimals()
							if cmd, err := RunAnimalDialog2(mw, animal); err != nil {
								log.Print(err)
							} else if cmd == walk.DlgCmdOK {
								if animal.Name == "" {
									mw.outTE.SetText("name is nil")
									return
								}
								mw.AddAnimals(animal)
								mw.Select(animal.Name)
								mw.outTE.SetText(fmt.Sprintf("%+v", animal))
								mw.model.AddItem(mw, "", animal.Name, animal)
								mw.slb.SetModel(NewEnvModel())
								conM(mw)
							}
						},
					},
					PushButton{
						Text: "当前连接信息",
						OnClicked: func() {
							animal := mw.GetCurAnimals()
							if animal == nil {
								animal = NewAnimals()
							}
							oldName := animal.Name
							if cmd, err := RunAnimalDialog2(mw, animal); err != nil {
								log.Print(err)
							} else if cmd == walk.DlgCmdOK {
								if animal.Name == "" {
									mw.outTE.SetText("name is nil")
									return
								}
								mw.outTE.SetText(fmt.Sprintf("%+v", animal))
								mw.model.AddItem(mw, oldName, animal.Name, animal)
								var mv *EnvModel
								var ok bool
								if mv, ok = mw.sub[oldName]; ok {
									delete(mw.sub, oldName)
									mw.sub[animal.Name] = mv
								} else {
									mv = NewEnvModel()
								}
								mw.slb.SetModel(mv)
							}
						},
					},
					VSplitter{
						MaxSize:    Size{150, 600},
						ColumnSpan: 1,
						Children: []Widget{
							HSplitter{
								RowSpan:    1,
								ColumnSpan: 2,
								Children: []Widget{
									PushButton{
										Text:       "连接",
										MaxSize:    Size{Width: 30},
										ColumnSpan: 1,
										OnClicked: func() {
											log.Print("Copy:连接 ")

										},
									},
									Label{
										Text:     "",
										AssignTo: &mw.curAnimalsLabel,
										MinSize: Size{
											Width: 90,
										},
										ColumnSpan: 1,
									},
								},
							},
							ListBox{
								RowSpan:    1,
								ColumnSpan: 1,
								AssignTo:   &mw.lb,
								Model:      mw.model,
								OnCurrentIndexChanged: func() {
									i := mw.lb.CurrentIndex()
									if i < 0 {
										return
									}
									item := &mw.model.items[i]
									v, ok := item.value.(*Animals)
									if !ok {
										mw.outTE.SetText("获取数据错误")
										return
									}
									mw.Select(v.Name)
									mw.curSub = -1
									if mv, ok := mw.sub[v.Name]; ok {
										mw.slb.SetModel(mv)
									} else {
										mw.slb.SetModel(NewEnvModel())
									}
									mw.outTE.SetText(fmt.Sprintf("%+v\n", v))
								},
							},
							Label{
								Background: SystemColorBrush{Color: walk.SysColorAppWorkspace},
								Text:       "By SiltIce In 2021.8",
							},
						},
					},
				},
			},

			Composite{
				ColumnSpan: 1,
				Background: SystemColorBrush{Color: walk.SysColorAppWorkspace},
				Layout:     VBox{},
				MaxSize:    Size{150, 100},
				MinSize:    Size{150, 100},
				OnSizeChanged: func() {
					mw.op.SetMinMaxSize(walk.Size(Size{150, 100}), walk.Size(Size{150, 100}))
				},
				Children: []Widget{
					VSplitter{
						ColumnSpan: 1,
						Row:        2,
						Children: []Widget{
							HSplitter{
								RowSpan: 1,
								Column:  2,
								Children: []Widget{
									PushButton{
										Text:       "Sub",
										ColumnSpan: 1,
										OnClicked: func() {
											log.Print("Copy:Subscription ")
											sub := &Sub{
												Qos: 0,
											}
											if mw.curAnimals == "" {
												mw.outTE.SetText("connect is establish")
												return
											}
											if cmd, err := RunAnimalDialogSub(mw, sub); err != nil {
												log.Print(err)
											} else if cmd == walk.DlgCmdOK {
												if sub.Topic == "" {
													mw.outTE.SetText("topic is nil")
													return
												}
												if sb, ok := mw.sub[mw.curAnimals]; ok {
													sb.AddEnd(mw, sub.Topic, sub)
													mw.outTE.SetText("topic has subscription")
													subM(mw, sub.Topic, sub.Qos, false)
													return
												} else {
													sb = NewEnvModel()
													sb.AddEnd(mw, sub.Topic, sub)
													mw.sub[mw.curAnimals] = sb
													mw.outTE.SetText("topic has subscription")
													subM(mw, sub.Topic, sub.Qos, false)
												}
												mw.outTE.SetText(fmt.Sprintf("%+v", sub))
											}
										},
									},
									PushButton{
										Text:       "UnSub",
										ColumnSpan: 1,
										OnClicked: func() {
											log.Print("Copy:UnSubscription ")
											if mw.curAnimals == "" {
												mw.outTE.SetText("connect is establish")
												return
											}
											fmt.Println(mw.curSub)
											if mw.curSub < 0 {
												mw.outTE.SetText("please click one topic")
												return
											}
											if sb, ok := mw.sub[mw.curAnimals]; ok {
												tpc := sb.Value(mw.curSub)
												if tpc == nil {
													return
												}
												sb.DeleteItem(mw, mw.curSub)
												mw.curSub = -1
												mw.outTE.SetText("topic has unsubscription")

												unSubM(mw, tpc.(string), false)
												return
											}
										},
									},
								},
							},
							ListBox{
								RowSpan:    1,
								AssignTo:   &mw.slb,
								ColumnSpan: 1,
								OnCurrentIndexChanged: func() {
									i := mw.slb.CurrentIndex()
									if i < 0 {
										return
									}
									mw.curSub = i // 保存当前点击选择的订阅
									fmt.Println(i)
									if sb, ok := mw.sub[mw.curAnimals]; !ok {
										return
									} else {
										mw.outTE.SetText(fmt.Sprintf("%+v\n", sb.items))
									}
								},
							},
						},
					},
				},
			},
			Composite{
				ColumnSpan: 3,
				Background: SystemColorBrush{Color: walk.SysColorAppWorkspace},
				Layout:     HBox{},

				Children: []Widget{
					VSplitter{
						Row: 6,
						DataBinder: DataBinder{
							AssignTo:       &mw.pubRec.DataBinder,
							Name:           "pubrec",
							DataSource:     &mw.pubRec,
							ErrorPresenter: ToolTipErrorPresenter{},
						},
						Children: []Widget{
							HSplitter{
								RowSpan: 1,
								Column:  5,
								Children: []Widget{
									ComboBox{
										Value:         Bind("PubContentType", SelRequired{}),
										BindingMember: "Id",
										DisplayMember: "Name",
										ColumnSpan:    1,
										Model:         ServerContentType(),
									},
									HSpacer{ColumnSpan: 1},
									PushButton{
										Text:       "All",
										ColumnSpan: 1,
										OnClicked: func() {
											mw.outTE.SetText(mw.curAnimals + " all")
										},
									},
									PushButton{
										Text:       "Received",
										ColumnSpan: 1,
										OnClicked: func() {
											mw.outTE.SetText(mw.curAnimals + " Received")
										},
									},
									PushButton{
										Text:       "Published",
										ColumnSpan: 1,
										OnClicked: func() {
											mw.outTE.SetText(mw.curAnimals + " Published")
										},
									},
								},
							},
							TextEdit{
								AssignTo: &mw.outTE,
								ReadOnly: true,
								RowSpan:  1,
								MaxSize:  Size{50, 600},
							},
							HSplitter{
								RowSpan:    1,
								ColumnSpan: 6,
								Children: []Widget{
									Label{
										Text:       "Payload:",
										ColumnSpan: 1,
									},
									ComboBox{
										Value:         Bind("PubPayload", SelRequired{}),
										BindingMember: "Id",
										DisplayMember: "Name",
										ColumnSpan:    1,
										Model:         ServerPayloadContentType(),
									},
									Label{
										Text:       "Qos:",
										ColumnSpan: 1,
									},
									ComboBox{
										Value:         Bind("PubQoS", SelRequired{}),
										BindingMember: "Id",
										DisplayMember: "Name",
										ColumnSpan:    1,
										Model:         ServerQoS(),
									},
									Label{
										Text:       "Retain:",
										ColumnSpan: 1,
									},
									ComboBox{
										Value:         Bind("PubRetain", SelRequired{}),
										BindingMember: "Id",
										DisplayMember: "Name",
										ColumnSpan:    1,
										Model:         ServerRetain(),
									},
								},
							},
							HSplitter{
								Column:  3,
								RowSpan: 1,
								Children: []Widget{
									Label{
										Text:       "topic:",
										ColumnSpan: 1,
									},
									LineEdit{
										Text:       Bind("PubTopic"),
										ColumnSpan: 2,
									},
								},
							},
							TextEdit{
								AssignTo: &mw.pubRec.Msg,
								ReadOnly: false,
								RowSpan:  1,
								MaxSize:  Size{50, 600},
							},
							PushButton{
								Text:    "发送",
								RowSpan: 1,
								OnClicked: func() {
									mw.outTE.SetText(mw.curAnimals + " 发送 " + mw.pubRec.Msg.Text())
									if err := mw.pubRec.DataBinder.Submit(); err != nil {
										log.Print(err)
									} else {
										mw.pubRec.PubBody = mw.pubRec.Msg.Text()
										log.Print(fmt.Sprintf("%+v\n", mw.pubRec))
										pubM(mw, mw.pubRec.PubTopic, mw.pubRec.PubQoS, mw.pubRec.PubBody)
									}
								},
							},
						},
					},
				},
			},
		},
	}.Run()); err != nil {
		log.Fatal(err)
	}
}

type Animals struct {
	Name     string
	ClientId string

	ServerProtocol string
	ServerAddr     string
	ServerPort     int

	Username string
	Password string

	SSLTLS bool

	ConnectExpiryTime int
	KeepAlive         int
	CleanSession      bool
	AutoRetry         bool
	Version           int
	SessionExpiryTime int
	ReceiverMax       int
	TopicAliceMax     int

	WillTopic              string
	WillQOS                int
	WillRetain             bool
	WillPayload            string
	ContextType            string
	PayloadFormatIndicator bool // 载荷格式指示
	WillMsgDelayTime       int
	WillMsgExpiryTime      int
	ResponseTopic          string
	CorrelationData        string

	AuthMethod string
	AuthData   string

	con     *servicev5.Client
	conOpen bool
}

func NewAnimals() *Animals {
	return &Animals{
		ServerProtocol:    "mqtt://",
		ServerAddr:        "127.0.0.1",
		ServerPort:        1883,
		ConnectExpiryTime: 30,
		KeepAlive:         30,
		SessionExpiryTime: 60,
		Version:           5,
		ClientId:          RandStr(10),
	}
}
func (a Animals) check() error {
	if a.Name == "" {
		return errors.New("name is nil")
	}
	return nil
}

type SpeciesPro struct {
	Id   string
	Name string
}
type SpeciesInt struct {
	Id   int
	Name string
}
type SpeciesBool struct {
	Id   bool
	Name string
}

func ServerMQTTVersion() []*SpeciesInt {
	return []*SpeciesInt{
		{5, "5.0"},
	}
}
func ServerQoS() []*SpeciesInt {
	return []*SpeciesInt{
		{0, "0"},
		{1, "1"},
		{2, "2"},
	}
}
func ServerRetain() []*SpeciesBool {
	return []*SpeciesBool{
		{true, "true"},
		{false, "false"},
	}
}
func ServerProtocol() []*SpeciesPro {
	return []*SpeciesPro{
		{"mqtt://", "mqtt://"},
		{"mqtts://", "mqtts://"},
		{"ws://", "ws://"},
		{"wss://", "wss://"},
	}
}
func ServerContentType() []*SpeciesPro {
	return []*SpeciesPro{
		{"Plaintext", "Plaintext"},
		{"Base64", "Base64"},
		{"JSON", "JSON"},
		{"Hex", "Hex"},
	}
}
func ServerPayloadContentType() []*SpeciesPro {
	return []*SpeciesPro{
		{"Plaintext", "Plaintext"},
		{"Base64", "Base64"},
		{"JSON", "JSON"},
		{"Hex", "Hex"},
	}
}
func RunAnimalDialog2(owner walk.Form, animal *Animals) (int, error) {
	var dlg *walk.Dialog
	var db *walk.DataBinder
	var acceptPB, cancelPB *walk.PushButton

	return Dialog{
		AssignTo:      &dlg,
		Title:         Bind("'Animal Details' + (animal.Name == '' ? '' : ' - ' + animal.Name)"),
		DefaultButton: &acceptPB,
		CancelButton:  &cancelPB,
		DataBinder: DataBinder{
			AssignTo:       &db,
			Name:           "animals",
			DataSource:     animal,
			ErrorPresenter: ToolTipErrorPresenter{},
		},
		MinSize: Size{320, 300},
		Layout:  VBox{},
		Children: []Widget{
			Composite{
				Layout:  Grid{Columns: 6},
				MaxSize: Size{700, 600},
				MinSize: Size{700, 600},
				Children: []Widget{
					Label{
						Text:       "名称:",
						ColumnSpan: 1,
					},
					LineEdit{
						Text:       Bind("Name"),
						ColumnSpan: 2,
					},

					Label{
						Text:       "Client ID:",
						ColumnSpan: 1,
					},
					LineEdit{
						Text:       Bind("ClientId"),
						ColumnSpan: 2,
					},

					Label{
						Text:       "服务器地址:",
						ColumnSpan: 1,
					},
					ComboBox{
						Value:         Bind("ServerProtocol", SelRequired{}),
						BindingMember: "Id",
						DisplayMember: "Name",
						ColumnSpan:    1,
						Model:         ServerProtocol(),
					},
					LineEdit{
						Text:       Bind("ServerAddr", SelRequired{}),
						ColumnSpan: 2,
					},

					Label{
						Text:       "端口:",
						ColumnSpan: 1,
					},
					NumberEdit{
						Value:      Bind("ServerPort", SelRequired{}),
						ColumnSpan: 1,
					},

					Label{
						Text:       "用户名:",
						ColumnSpan: 1,
					},
					LineEdit{
						Text:       Bind("Username"),
						ColumnSpan: 2,
					},
					Label{
						Text:       "密码:",
						ColumnSpan: 1,
					},
					LineEdit{
						Text:       Bind("Password"),
						ColumnSpan: 2,
					},

					RadioButtonGroupBox{
						ColumnSpan: 2,
						Title:      "SSL/TLS",
						Layout:     HBox{},
						DataMember: "SSLTLS",
						Buttons: []RadioButton{
							{Text: "true", Value: true},
							{Text: "false", Value: false},
						},
					},
					RadioButtonGroupBox{
						ColumnSpan: 2,
						Title:      "清除会话",
						Layout:     HBox{},
						DataMember: "CleanSession",
						Buttons: []RadioButton{
							{Text: "true", Value: true},
							{Text: "false", Value: false},
						},
					},
					RadioButtonGroupBox{
						ColumnSpan: 2,
						Title:      "自动重连",
						Layout:     HBox{},
						DataMember: "AutoRetry",
						Buttons: []RadioButton{
							{Text: "true", Value: true},
							{Text: "false", Value: false},
						},
					},

					Label{
						Text:       "连接超时时长 (秒):",
						ColumnSpan: 1,
					},
					NumberEdit{
						Value:      Bind("ConnectExpiryTime", SelRequired{}),
						ColumnSpan: 2,
					},
					Label{
						Text:       "Keep Alive (秒):",
						ColumnSpan: 1,
					},
					NumberEdit{
						Value:      Bind("ConnectExpiryTime", SelRequired{}),
						ColumnSpan: 2,
					},

					Label{
						Text:       "MQTT 版本:",
						ColumnSpan: 1,
					},
					ComboBox{
						Value:         Bind("Version", SelRequired{}),
						BindingMember: "Id",
						DisplayMember: "Name",
						ColumnSpan:    1,
						Model:         ServerMQTTVersion(),
					},
					Label{
						Text:       "^-^",
						ColumnSpan: 4,
					},

					Label{
						Text:       "会话过期时间:",
						ColumnSpan: 1,
					},
					NumberEdit{
						Value:      Bind("SessionExpiryTime", SelRequired{}),
						ColumnSpan: 1,
					},

					Label{
						Text:       "接收最大数值:",
						ColumnSpan: 1,
					},
					NumberEdit{
						Value:      Bind("ReceiverMax", SelRequired{}),
						ColumnSpan: 1,
					},

					Label{
						Text:       "主题别名最大值:",
						ColumnSpan: 1,
					},
					NumberEdit{
						Value:      Bind("TopicAliceMax", SelRequired{}),
						ColumnSpan: 1,
					},

					Label{
						Text:       "遗嘱消息主题:",
						ColumnSpan: 1,
					},
					LineEdit{
						Text:       Bind("WillTopic"),
						ColumnSpan: 5,
					},

					RadioButtonGroupBox{
						ColumnSpan: 3,
						Title:      "遗嘱消息 QoS",
						Layout:     HBox{},
						DataMember: "WillQOS",
						Buttons: []RadioButton{
							{Text: "0", Value: 0},
							{Text: "1", Value: 1},
							{Text: "2", Value: 2},
						},
					},

					RadioButtonGroupBox{
						ColumnSpan: 3,
						Title:      "遗嘱消息保留标志",
						Layout:     HBox{},
						DataMember: "WillRetain",
						Buttons: []RadioButton{
							{Text: "true", Value: true},
							{Text: "false", Value: false},
						},
					},
					Label{
						Text:       "遗嘱消息载荷:",
						ColumnSpan: 1,
					},
					TextEdit{
						MaxSize:    Size{Height: 5},
						ColumnSpan: 2,
						Name:       "",
						Text:       Bind("WillPayload", SelRequired{}),
					},
					RadioButtonGroupBox{
						ColumnSpan: 3,
						Title:      "载荷内容类型",
						Layout:     HBox{},
						DataMember: "ContextType",
						Buttons: []RadioButton{
							{Text: "JSON", Value: "JSON"},
							{Name: "check", Text: "Plaintext", Value: "Plaintext"},
						},
					},
					RadioButtonGroupBox{
						ColumnSpan: 6,
						Title:      "是否为 UTF-8 编码数据",
						Layout:     HBox{},
						DataMember: "PayloadFormatIndicator",
						Buttons: []RadioButton{
							{Text: "true", Value: true},
							{Text: "false", Value: false},
						},
					},

					Label{
						Text:       "遗嘱消息延迟时间 (秒):",
						ColumnSpan: 1,
					},
					NumberEdit{
						Value:      Bind("WillMsgDelayTime", SelRequired{}),
						ColumnSpan: 2,
					},
					Label{
						Text:       "遗嘱消息过期时间 (秒):",
						ColumnSpan: 1,
					},
					NumberEdit{
						Value:      Bind("WillMsgExpiryTime", SelRequired{}),
						ColumnSpan: 2,
					},

					Label{
						Text:       "响应主题:",
						ColumnSpan: 1,
					},
					LineEdit{
						Text:       Bind("ResponseTopic"),
						ColumnSpan: 2,
					},
					Label{
						Text:       "对比数据:",
						ColumnSpan: 1,
					},
					LineEdit{
						Text:       Bind("CorrelationData"),
						ColumnSpan: 2,
					},

					Label{
						Text:       "认证方法:",
						ColumnSpan: 1,
					},
					LineEdit{
						Text:       Bind("AuthMethod"),
						ColumnSpan: 2,
					},
					Label{
						Text:       "认证数据:",
						ColumnSpan: 1,
					},
					LineEdit{
						Text:       Bind("AuthData"),
						ColumnSpan: 2,
					},
				},
			},
			Composite{
				Layout: HBox{},
				Children: []Widget{
					HSpacer{},
					PushButton{
						AssignTo: &acceptPB,
						Text:     "OK",
						OnClicked: func() {
							if err := db.Submit(); err != nil {
								log.Print(err)
								return
							}

							dlg.Accept()
						},
					},
					PushButton{
						AssignTo:  &cancelPB,
						Text:      "Cancel",
						OnClicked: func() { dlg.Cancel() },
					},
				},
			},
		},
	}.Run(owner)
}

func RunAnimalDialogSub(owner walk.Form, sub *Sub) (int, error) {
	var dlg *walk.Dialog
	var db *walk.DataBinder
	var acceptPB, cancelPB *walk.PushButton

	return Dialog{
		AssignTo:      &dlg,
		Title:         Bind("'Sub Details' + (animal.Name == '' ? '' : ' - ' + animal.Name)"),
		DefaultButton: &acceptPB,
		CancelButton:  &cancelPB,
		DataBinder: DataBinder{
			AssignTo:       &db,
			Name:           "sub",
			DataSource:     sub,
			ErrorPresenter: ToolTipErrorPresenter{},
		},
		MinSize: Size{200, 100},
		Layout:  VBox{},
		Children: []Widget{
			Composite{
				Layout:  Grid{Columns: 2},
				MaxSize: Size{200, 100},
				MinSize: Size{200, 100},
				Children: []Widget{
					Label{
						Text:       "Topic:",
						ColumnSpan: 1,
					},
					LineEdit{
						Text:       Bind("Topic"),
						ColumnSpan: 1,
					},

					Label{
						Text:       "Qos:",
						ColumnSpan: 1,
					},
					ComboBox{
						Value:         Bind("Qos", SelRequired{}),
						BindingMember: "Id",
						DisplayMember: "Name",
						ColumnSpan:    1,
						Model:         ServerQoS(),
					},
					Label{
						Text:       "Alias:",
						ColumnSpan: 1,
					},
					NumberEdit{
						Value:      Bind("Alias"),
						ColumnSpan: 1,
					},
				},
			},
			Composite{
				Layout: HBox{},
				Children: []Widget{
					HSpacer{},
					PushButton{
						AssignTo: &acceptPB,
						Text:     "OK",
						OnClicked: func() {
							if err := db.Submit(); err != nil {
								log.Print(err)
								return
							}

							dlg.Accept()
						},
					},
					PushButton{
						AssignTo:  &cancelPB,
						Text:      "Cancel",
						OnClicked: func() { dlg.Cancel() },
					},
				},
			},
		},
	}.Run(owner)
}

type EnvItem struct {
	name  string
	value interface{}
}

type EnvModel struct {
	walk.ListModelBase
	items []EnvItem
}

func NewEnvModel() *EnvModel {
	m := &EnvModel{items: make([]EnvItem, 0)}
	return m
}

// 订阅列表使用
func (m *EnvModel) AddEnd(mw *MainWind, name string, value interface{}) {
	m.items = append(m.items, EnvItem{
		name:  name,
		value: value,
	})
	mw.slb.SetModel(m)
}
func (m *EnvModel) DeleteItem(mw *MainWind, in int) {
	tmp := make([]EnvItem, 0)
	tmp = append(tmp, m.items[:in]...)
	if in <= len(m.items)-1 {
		tmp = append(tmp, m.items[in+1:]...)
	}
	m.items = tmp
	mw.slb.SetModel(m)
}

// 连接列表使用
func (m *EnvModel) AddItem(mw *MainWind, oldName, name string, value interface{}) {
	if oldName != "" {
		for i := 0; i < len(m.items); i++ {
			if m.items[i].name == name {
				mw.outTE.SetText("名称重复")
				return
			} else if m.items[i].name == oldName {
				m.items[i].value = value
				m.items[i].name = name
				mw.lb.SetModel(m)
				return
			}
		}
	}
	defer func() {
		mw.lb.SetModel(mw.model)
	}()
	for i := 0; i < len(m.items); i++ {
		if m.items[i].name == name {
			m.items[i].value = value
			return
		}
	}
	//from := len(m.items)
	m.items = append(m.items, EnvItem{
		name:  name,
		value: value,
	})
	//m.PublishItemsInserted(from, len(m.items))
}
func (m *EnvModel) ItemCount() int {
	return len(m.items)
}

func (m *EnvModel) Value(index int) interface{} {
	return m.items[index].name
}
